
// Copyright (C) 2007 Richard Uhler
// This file is part of Esiply.
//
// Esiply is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License, Version 2,
// as published by the Free Software Foundation.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

#include <assert.h>
#include <iostream>
#include <stdio.h>
#include "Boolean.h"
#include "Number.h"
#include "Object.h"
#include "String.h"
#include "Undefined.h"
#include "Value.h"

Value::Value()
{}

Value::Value(const Value& rhs)
{
    assert(false && "Don't call Value's copy constructor!");
}

Value& Value::operator=(const Value& rhs)
{
    assert(false && "Don't call Value's assign operator!");
    return *this;
}

Value* Value::evaluate(Scope* scope)
{
    return this;
}

String* Value::toString()
{
    // This means we forgot to implement the toString.
    fprintf(stderr, "Tried to call toString on type %i\n", getType());
    assert(false);
    return new String("???");
}

Value* Value::toPrimitive()
{
    return this;
}

Value* Value::toPrimitive(ValueType hint)
{
    return toPrimitive();
}

Boolean* Value::toBoolean()
{
    // This means the subclass of value hasn't yet implemented
    // its toBoolean method, or that method should never be called.
    assert(false && "Calling toBoolean on a type which hasn't implemented it");
    return Boolean::False;
}

Number* Value::toNumber()
{
    // This means the subclass of Value hasn't yet
    // implemented its toNumber method
    fprintf(stderr, "Value of type %i hasn't implemented toNumber.\n", 
            getType());
    assert(false);
    return NULL;
}

Object* Value::toObject()
{
    // not implemented.
    fprintf(stderr,
            "Value of type %i trying to convert itself to an object.\n",
            getType());
    assert(false);
    return NULL;
}

Number* Value::toInteger()
{
    // Ecma 9.4
    // 1. Call to number on the input argument.
    // Return it's toInteger
    return this->toNumber()->toInt32();
}

Number* Value::toInt32()
{
    // Ecma 9.5
    // 1. Call to number on the input argument.
    // Return it's toInt32
    return this->toNumber()->toInt32();
}

Number* Value::toUint16()
{
    // Ecma 9.7
    // 1. Call tonumber on the input arguments.
    // Return it's toUint16
    return this->toNumber()->toUint16();
}

Number* Value::toUint32()
{
    // Ecma 9.6
    // 1. call to number on the input argument.
    // Return it's toUint32
    return this->toNumber()->toUint32();
}

Value* Value::getValue()
{
    return this;
}

void Value::putValue(Value* value)
{
    // Ecma 8.7.2
    // 1. If Type is not Reference, throw a ReferenceError
    // TODO: but how? we don't return anything...
    //return esiply_throw(ET_TYPE, "Calling put value on a non-reference type");
    assert(false && "Calling put value on a non-reference type");
}

bool Value::isException() const
{
    return false;
}

Object* Value::object() const
{
    assert(this->getType() == VT_OBJECT);
    return (Object*)this;
}

Number* Value::number() const
{
    assert(this->getType() == VT_NUMBER);
    return (Number*)this;
}

String* Value::string() const
{
    assert(this->getType() == VT_STRING);
    return (String*)this;
}

Boolean* Value::boolean() const
{
    assert(this->getType() == VT_BOOLEAN);
    return (Boolean*)this;
}

Function* Value::function() const
{
    assert(this->getType() == VT_OBJECT);
    assert(this->object()->isCallable());
    return (Function*)this;
}

List* Value::list() const
{
    assert(this->getType() == VT_LIST);
    return (List*)this;
}

Completion* Value::completion() const
{
    assert(this->getType() == VT_COMPLETION);
    return (Completion*)this;
}

Reference* Value::reference() const
{
    assert(this->getType() == VT_REFERENCE);
    return (Reference*)this;
}

Boolean* Value::less_than(Value* lhs, Value* rhs)
{
    // Ecma 11.8.5 The Abstract Relational Comparison Algorithm
    // 1. Call toPrimitive lhs, hint Number
    // 2. call toPrimitive rhs, hint Number
    Value* x = lhs->toPrimitive(VT_NUMBER);
    Value* y = rhs->toPrimitive(VT_NUMBER);

    // 3. If both types string, goto step 16
    if (x->getType() == VT_STRING && y->getType() == VT_STRING)
    {
        // 16. Return the results of string comparison 
        // (see that operator)
        return String::less_than(x->string(), y->string());
    }

    // 4, 5. Call to number on x, y
    // 6. Return the results of number comparison
    // (see that operator)
    return Number::less_than(x->number(), y->number());
}

Boolean* Value::equals(Value* lhs, Value* rhs)
{
    // Ecma 11.9.3 The Abstract Equality Comparison Algorithm
    Value* x = lhs;
    Value* y = rhs;

    ValueType type_x = x->getType();
    ValueType type_y = y->getType();

    // 1. If type of x is different from type of y, goto 14
    if (type_x != type_y)
    {
        // 14. If x is null and y is undefined, return true.
        if (type_x == VT_NULL && type_y == VT_UNDEFINED)
        {
            return Boolean::True;
        }

        // 15. If x is undefined and y is null, return true.
        if (type_x == VT_UNDEFINED && type_y == VT_NULL)
        {
            return Boolean::True;
        }

        // 16. If type of x is Number, and type of y is string
        // return the results of x == toNumber(y)
        if (type_x == VT_NUMBER && type_y == VT_STRING)
        {
            return Number::equals(x->number(), y->toNumber());
        }

        // 17. If type x is String and type y Number
        // return the results of toNumber(x) == y
        if (type_x == VT_STRING && type_y == VT_NUMBER)
        {
            return Number::equals(x->toNumber(), y->number());
        }

        // 18. if type x is Boolean, return the result of the
        // comparison toNumber(x) == y
        if (type_x == VT_BOOLEAN)
        {
            return Value::equals(x->toNumber(), y);
        }

        // 19. if type y is boolean, return the result of x == toNumber(y)
        if (type_y == VT_BOOLEAN)
        {
            return Value::equals(x, y->toNumber());
        }

        // 20. if x is either String or Number, and y is object
        // return the result of x == toPrimitive(y)
        if ((type_x == VT_STRING || type_x == VT_NUMBER) && type_y == VT_OBJECT)
        {
            return Value::equals(x, y->toPrimitive());
        }

        // 21. if type x is Object, type y is String or Number
        // return toPrimitive(x) == y
        if (type_x == VT_OBJECT && (type_y == VT_STRING || type_y == VT_NUMBER))
        {
            return Value::equals(x->toPrimitive(), y);
        }

        // 22. return false
        return Boolean::False;
    }

    // 2. if type x is undefined, return true
    // 3. if type is Null, return true
    if (type_x == VT_UNDEFINED || type_x == VT_NULL)
    {
        return Boolean::True;
    }

    // 4. if type is not number, goto 11
    if (type_x != VT_NUMBER)
    {
        // 11. if type is string, compare those
        if (type_x == VT_STRING)
        {
            return String::equals(x->string(), y->string());
        }

        // 12. if bool, compare those
        if (type_x == VT_BOOLEAN)
        {
            return Boolean::equals(x->boolean(), y->boolean());
        }

        // 13. return true if x and y refer to the same object, or if they
        // refer to objects joined to each other, otherwise return false.
        return x->object() == y->object() ? Boolean::True : Boolean::False;
    }

    // 5. return the results of numeric comparison
    return Number::equals(x->number(), y->number());
}

Value* Value::add(Value* lhs, Value* rhs)
{
    // Ecma 11.6.1.
    Value* x = lhs;

    Value* y = rhs;

    // 5, 6. Call toPrimitive on those results
    x = x->toPrimitive();

    y = y->toPrimitive();

    // 7. If type of x is String or Type of y is String, goto  step 12
    if (x->getType() == VT_STRING || y->getType() == VT_STRING)
    {
        // 12. Call toString on x
        // 13. Call toString on y
        // 14. Concatenate x, y
        // 15. Return the result
        return String::add(x->toString(), y->toString());
    }

    // 8, 9. Call toNumber on x, y
    // 10. Apply the addition operation
    // 11. Return the result
    return Number::add(x->toNumber(), y->toNumber());
}

Value* Value::number_operate(Value* lhs, Value* rhs, binary_numberop_t op)
{
    return (*op)(lhs->toNumber(), rhs->toNumber());
}

Value* Value::subtract(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::subtract);
}
    
Value* Value::multiply(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::multiply);
}

Value* Value::divide(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::divide);
}

Value* Value::modulo(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::modulo);
}

Value* Value::left_shift(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::left_shift);
}

Value* Value::right_shift(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::right_shift);
}

Value* Value::unsigned_right_shift(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::unsigned_right_shift);
}

Value* Value::bitwise_and(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::bitwise_and);
}

Value* Value::bitwise_or(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::bitwise_or);
}

Value* Value::bitwise_xor(Value* lhs, Value* rhs)
{
    return number_operate(lhs, rhs, &Number::bitwise_xor);
}

Boolean* Value::strict_equals(Value* lhs, Value* rhs)
{
    // Ecma 11.9.6
    Value* x = lhs;
    Value* y = rhs;

    // 1. If type of x is different from type of y, return false
    if (x->getType() != y->getType())
    {
        return Boolean::False;
    }

    switch (x->getType())
    {
    case VT_UNDEFINED:
    case VT_NULL:
        // 2. If type of x is undefined, return true.
        // 3. If type of x is null, return true
        return Boolean::True;

    case VT_NUMBER:
        // 4. if type of x is not a number, go to step 11
        // 5-10, number specific
        return Number::equals(x->number(), y->number());

    case VT_STRING:
        // 11. if type of x is string, sring specific
        return String::equals(x->string(), y->string());

    case VT_BOOLEAN:
        // 12. If type of x is boolean, boolean specific
        return Boolean::equals(x->boolean(), y->boolean());

    case VT_OBJECT:
        // 13. return true if x and y refer to the same object.
        // Otherwise return false
        return x->object() == y->object() ? Boolean::True : Boolean::False;

    default:
        assert(false);
    }

    // We should never get here
    assert(false);
    return NULL;
}

